package com.rpoonolly.simplegame.editor;

import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.vecmath.Point2d;
import javax.vecmath.Vector2d;

import com.rpoonolly.simplegame.Config;
import com.rpoonolly.simplegame.SimpleUserAction;
import com.rpoonolly.simplegame.animation.TexturedDynamicEntity;
import com.rpoonolly.simplegame.core.CollisionDetector;
import com.rpoonolly.simplegame.core.CollisionResponder;
import com.rpoonolly.simplegame.core.CountingInputManager;
import com.rpoonolly.simplegame.core.GameLogicProcessor;
import com.rpoonolly.simplegame.core.MapFileManager;
import com.rpoonolly.simplegame.core.aabb.AABBCollision;
import com.rpoonolly.simplegame.core.aabb.AABBEntity;
import com.rpoonolly.simplegame.core.aabb.DynamicAABBEntity;
import com.rpoonolly.simplegame.textures.TextureId;
import com.rpoonolly.simplegame.textures.TextureLoader;
import com.rpoonolly.simplegame.worldGrid.WorldGrid;
import com.rpoonolly.simplegame.worldGrid.WorldGridGameMap;
import com.rpoonolly.simplegame.worldGrid.WorldGridTile;

public class SimpleEditorProcessor implements GameLogicProcessor, WindowListener {
	private static final Vector2d ZERO_VECTOR = new Vector2d();
	private static final String[] DELETABLE_ENTITIES = { Config.ENT_GRP_ENEMIES, Config.ENT_GRP_GHOSTS, Config.ENT_GRP_WORLD_OBJS };
	
	protected CollisionDetector<AABBEntity, AABBCollision> collisionDetector;
	protected CollisionResponder<AABBCollision> collisionResponder;
	protected MapFileManager mapLoader;
	protected CountingInputManager<SimpleUserAction> inputMngr;
	protected WorldGridGameMap map;
	protected Map<String, List<AABBEntity>> entities;
	protected WorldGrid grid;
	
	private AABBEntity tmpWorldObj;
	private TexturedDynamicEntity tmpEnemy;
	
	private Point2d pivot = new Point2d();
	private Point2d cursor = new Point2d();
	private AABBEntity tileSelection = new AABBEntity();
	
	private EditorPanel editorPanel;
	
	public SimpleEditorProcessor(
		CollisionDetector<AABBEntity, AABBCollision> detector, 
		CollisionResponder<AABBCollision> responder,
		WorldGridGameMap gameMap,
		CountingInputManager<SimpleUserAction> inputMngr,
		MapFileManager mapFileMngr,
		TextureLoader textureLoader
	) {
		collisionDetector = detector;
		collisionResponder = responder;
		this.inputMngr = inputMngr;
		
		this.map = gameMap;
		this.entities = gameMap.getEntities();
		this.grid = gameMap.getGrid();
		this.mapLoader = mapFileMngr;
		
		editorPanel = new EditorPanel(textureLoader);
		editorPanel.addWindowListener(this);
	}
	
	@Override
	public void process() {
		if (inputMngr.getActionCount(SimpleUserAction.EDITOR_TOGGLE_EDIT_MODE) != 0) {
			toggleEditMode();
			inputMngr.clearActionCount(SimpleUserAction.EDITOR_TOGGLE_EDIT_MODE);
		}
		
		if (!isEditModeEnabled()) return;
		
		DynamicAABBEntity player = (DynamicAABBEntity) entities.get(Config.ENT_GRP_PLAYER).get(0);
		cursor.x = player.getX() + (player.getWidth() / 2);
		cursor.y = player.getY() + (player.getHeight() / 2);
		
		int tileCursorX = (int) cursor.x;
		int tileCursorY = (int) cursor.y;
		tileSelection.setX(tileCursorX - (tileCursorX % grid.getGridCellWidth()));
		tileSelection.setY(tileCursorY - (tileCursorY % grid.getGridCellHeight()));
		
		processEnemyCreateAction();
		processWorldObjCreateAction();
		processEntityDeleteAction();
		processTilePaintAction();
		processTileClearAction();
		processSaveAction();
		
		if (tmpWorldObj != null) scaleEntityFromPivotToCursor(tmpWorldObj, pivot, cursor);
		else if (tmpEnemy != null) setEntityPositionAtCursor(tmpEnemy, cursor);
	}
	
	private void processTileClearAction() {
		List<WorldGridTile> tilesToPaint;
		List<TextureId> textureStack;
		
		if (!inputMngr.isActionRequested(SimpleUserAction.EDITOR_TILE_CLEAR))	//inputMngr.getActionCount(SimpleUserAction.EDITOR_TILE_CLEAR) == 0)
			return;
		
		tilesToPaint = grid.getCollidingTiles(tileSelection);
		
		for(WorldGridTile tile : tilesToPaint) {
			textureStack = tile.getTextures();
			if (textureStack != null) textureStack.clear();
		}
		
//		inputMngr.clearActionCount(SimpleUserAction.EDITOR_TILE_PAINT);
	}
	
	private void processTilePaintAction() {
		TextureId texture;
		List<WorldGridTile> tilesToPaint;
		List<TextureId> textureStack;
		
		if (!inputMngr.isActionRequested(SimpleUserAction.EDITOR_TILE_PAINT))	//inputMngr.getActionCount(SimpleUserAction.EDITOR_TILE_PAINT) == 0)
			return;
		
		tilesToPaint = grid.getCollidingTiles(tileSelection);
		texture = editorPanel.getSelectedTexture();
		
		for(WorldGridTile tile : tilesToPaint) {
			//FIXME: This should be moved into a function within WorldGrid or somewhere else
			textureStack = tile.getTextures();
			
			if (textureStack == null) {
				textureStack = new CopyOnWriteArrayList<TextureId>();
				textureStack.add(texture);
				tile = new WorldGridTile(tile.getCoordinate(), textureStack);
				grid.putTile(tile);
			} else {
				textureStack.add(texture);
			}
		}
		
//		inputMngr.clearActionCount(SimpleUserAction.EDITOR_TILE_PAINT);
	}
	
	private void processSaveAction() {
		if (inputMngr.getActionCount(SimpleUserAction.EDITOR_SAVE) == 0)
			return;
		
		try {
			mapLoader.writeMap(map, Config.DEFAULT_MAP_NAME);
			System.out.println("Map written to: " + Config.DEFAULT_MAP_NAME);
		} catch(IOException e) {
			e.printStackTrace();
		}
		
		inputMngr.clearActionCount(SimpleUserAction.EDITOR_SAVE);
	}
	
	private void processEntityDeleteAction() {
		List<AABBEntity> entityGroup;
		DynamicAABBEntity player;
		
		if (!inputMngr.isActionRequested(SimpleUserAction.EDITOR_DELETE))
			return;
		
		player = (DynamicAABBEntity) entities.get(Config.ENT_GRP_PLAYER).get(0);
		
		for(String groupName : DELETABLE_ENTITIES) {
			entityGroup = entities.get(groupName);
			for(AABBEntity entity : entityGroup)
				if (collisionDetector.collisionDetect(entity, player, ZERO_VECTOR) != null && 
						entity != tmpEnemy && entity != tmpWorldObj)
					entityGroup.remove(entity);
		}
	}
	
	private void processEnemyCreateAction() {
		List<AABBEntity> enemies;
		DynamicAABBEntity player;
		
		if (inputMngr.getActionCount(SimpleUserAction.EDITOR_CREATE_ENEMY) == 0)
			return;
		
		enemies = entities.get(editorPanel.getSelectedEntityType());
		player = (DynamicAABBEntity) entities.get(Config.ENT_GRP_PLAYER).get(0);
		
		if (tmpEnemy == null) {
			tmpEnemy = new TexturedDynamicEntity(player.getX(), player.getY(), Config.ENEMY_WIDTH, Config.ENEMY_HEIGHT);
			enemies.add(tmpEnemy);
		} else {
			tmpEnemy = null;
		}
		
		inputMngr.clearActionCount(SimpleUserAction.EDITOR_CREATE_ENEMY);
	}
	
	private void processWorldObjCreateAction() {
		List<AABBEntity> worldObjs;
		DynamicAABBEntity player;
		
		if (inputMngr.getActionCount(SimpleUserAction.EDITOR_CREATE_WORLD_OBJ) == 0)
			return;
		
		worldObjs = entities.get(Config.ENT_GRP_WORLD_OBJS);
		player = (DynamicAABBEntity) entities.get(Config.ENT_GRP_PLAYER).get(0);
		
		if (tmpWorldObj == null) {
			pivot.x = cursor.x;
			pivot.y = cursor.y;
			
			tmpWorldObj = new AABBEntity((int) pivot.x, (int) pivot.y, 0, 0);
			worldObjs.add(tmpWorldObj);
		} else {
			tmpWorldObj = null;
		}
		
		inputMngr.clearActionCount(SimpleUserAction.EDITOR_CREATE_WORLD_OBJ);
	}
	
	private void scaleEntityFromPivotToCursor(AABBEntity entity, Point2d pivot, Point2d cursor) {
		entity.setX((int) Math.min(pivot.x, cursor.x));
		entity.setY((int) Math.min(pivot.y, cursor.y));
		entity.setWidth((int) Math.abs(pivot.x - cursor.x));
		entity.setHeight((int) Math.abs(pivot.y - cursor.y));
	}
	
	private void setEntityPositionAtCursor(AABBEntity entity, Point2d cursor) {
		entity.setX((int) cursor.x);
		entity.setY((int) cursor.y);
	}
	
	private void toggleEditMode() {
		System.out.println("Enabling Edit Mode");
		editorPanel.setVisible(!editorPanel.isVisible());
		Config.isEditModeEnabled.set(!Config.isEditModeEnabled.get());
	}
	
	private boolean isEditModeEnabled() {
		return Config.isEditModeEnabled.get();
	}
	
	@Override
	public void windowClosing(WindowEvent e) {
		toggleEditMode();
	}

	@Override
	public void windowActivated(WindowEvent arg0) {}
	@Override
	public void windowClosed(WindowEvent arg0) {}
	@Override
	public void windowDeactivated(WindowEvent arg0) {}
	@Override
	public void windowDeiconified(WindowEvent arg0) {}
	@Override
	public void windowIconified(WindowEvent arg0) {}
	@Override
	public void windowOpened(WindowEvent arg0) {}
}
